import dayjs from 'dayjs'

// 一天的毫秒数
export const MS_PER_DAY = 24 * 60 * 60 * 1000; // 更清晰的命名，自解释

// 是否 dayjs 对象
export const isDayjsObject = v => (typeof v == 'object' ? dayjs().constructor === v.constructor : false)

// 是否 ISO_8601 日期格式字符串 —— 改用 dayjs 验证，更健壮
export const isIso8601 = v => typeof v === 'string' && dayjs(v, 'YYYY-MM-DDTHH:mm:ssZ', true).isValid()

// 是否空对象 —— 修复：排除 null/array/date 等
const isEmptyObject = v =>
  v != null &&
  typeof v === 'object' &&
  !Array.isArray(v) &&
  !(v instanceof Date) &&
  Object.keys(v).length === 0

// 是否空 —— 优化逻辑，避免重复计算
const isEmpty = (v) => {
  if (v == null) return true
  if (typeof v === 'number') return isNaN(v)
  if (typeof v === 'string') return v.trim() === ''
  if (Array.isArray(v)) return v.length === 0
  if (typeof v === 'object') return isEmptyObject(v)
  return false
}

/**
 * @desc  模板引擎（重写：避免死循环，提升性能与安全性）
 * @param  {String}           tpl    模板字符串
 * @param  {Object|Function}  data   替换数据或自定义替换函数
 * @param  {Object}           conf   配置：{_s, _e, _re, ...state}
 * @return {String}
 */
export const templateEngine = (tpl, data, conf = {}) => {
  const { _s = '#{', _e = '}', _re, ...state } = typeof conf === 'object' ? conf : {}
  const re = _re || new RegExp(`${_s}([^${_e}]+?)${_e}`, 'g')

  return tpl
    .replaceAll(_s + _e, '') // 清除空占位如 #{}
    .replace(re, (match, expr) => {
      expr = expr.trim()
      if (!expr) return match

      try {
        if (typeof data === 'function') {
          return String(data(expr, { match, expr, _s, _e, state }))
        }

        // 安全路径访问：过滤 __proto__/constructor 防原型污染
        const keys = expr
          .split('.')
          .map(s => s.trim())
          .filter(k => k && !/^__|constructor$/i.test(k))

        let val = data
        for (const key of keys) {
          if (val == null || typeof val !== 'object') return ''
          val = val[key]
        }
        return val == null ? '' : String(val)
      } catch (e) {
        console.warn(`Template "${expr}" error:`, e.message)
        return match // 或返回 ''，按需调整
      }
    })
}

/* ... 省略未修改的注释 ... */

export const methods = {
  undefined: { f: v => v, kw: ['_'] },
  /* 简写: {f: 格式化函数 ,kw:[ 关联关键字 ]} */
  0: { f: (v, len) => String(v).padStart(len, '0'), kw: ['zero'] },
  '+': { f: (v, ...args) => args.reduce((prev, cur) => Number(prev) + Number(cur), Number(v)) },
  '-': { f: (v, ...args) => args.reduce((prev, cur) => Number(prev) - Number(cur), Number(v)) },
  '*': { f: (v, ...args) => args.reduce((prev, cur) => Number(prev) * Number(cur), Number(v)) },
  '/': { f: (v, ...args) => args.reduce((prev, cur) => Number(prev) / Number(cur), Number(v)) },
  n: { f: (num, len) => len && len > 0 ? Number(num).toString().replace(/\d+/, n => n.replace(new RegExp(String.raw`(\d)(?=(?:\d{${len}})+$)`, 'g'), '$1,')) : +num, kw: ['number', 'num'] },
  f: { f: (val, fx) => (fx ? (+val).toFixed(Number(fx || 0)) : val), kw: ['float'] },
  p: { f: (val, fx, c) => parseFloat((val * 100).toFixed(Number(fx || 0))) + (c || '%'), kw: ['percentage', 'per', '%'] },
  d: { f: (val, f) => dayjs(+val).format(f == '0' || f ? ['YYYY-MM-DD', 'YYYY/MM/DD', 'YYYY-MM-DD HH:mm', 'YYYY-MM-DD HH:mm:ss', 'YYYY/MM/DD HH:mm', 'YYYY/MM/DD HH:mm:ss'][f] || f : 'YYYY-MM-DD HH:mm:ss'), kw: ['date', 'datetime'] },
  
  // ✅ 修复 wk：使用 val 计算，修正星期数组（7字符）
  wk: {
    f: (val, zh) => {
      const dayIndex = dayjs(val).day() // 0=Sun, 1=Mon, ..., 6=Sat
      return zh ? '日一二三四五六'[dayIndex] : dayIndex
    },
    kw: ['weekday']
  },
  
  up: { f: v => String(v).toUpperCase(), kw: ['upperCase'] },
  low: { f: v => String(v).toLowerCase(), kw: ['lowerCase'] },
  len: { f: v => (Array.isArray(v) || typeof v == 'string' || v?.length != null ? v.length : 0), kw: ['length'] },
  
  // ✅ ts 改名建议：保留但加注释（实际建议改名 trunc）
  // ts: { f: (v, exact) => exact ? Math.floor(+v / 10 ** exact) * 10 ** exact : +v, kw: [] /* trunc precision */ },
  ts: { f: (v, exact) => exact ? Math.floor(+v / 10 ** exact) * 10 ** exact : +v, kw: [] },
  
  unix: { f: v => +dayjs(v).endOf('day').unix(), kw: ['unixTimeStamp'] },
  padStart: { f: (v, ...args) => String(v).padStart(args[0], args[1] || ' ') },
  padEnd: { f: (v, ...args) => String(v).padEnd(args[0], args[1] || ' ') },
  
  // ✅ 修复 attr：不再用 isEmpty 过滤 0/false 等，改用 in 检查属性存在性
  attr: {
    f: (v, keys, str = null) => {
      const path = keys.split('.').map(s => s.trim()).filter(k => k && !/^__|constructor$/i.test(k))
      let val = v
      for (const key of path) {
        if (val == null || typeof val !== 'object') return str
        val = val[key]
      }
      return val == null ? str : val
    },
    kw: ['prop', 'field']
  },
  
  join: { f: (v, c = '-') => (Array.isArray(v) ? v.join(c) : v.replace(/([A-Z])/g, c + '$1').toLowerCase()) },
  hump: { f: v => v.replace(/-(\w)/g, (_, c) => (c ? c.toUpperCase() : '')) },
  
  // ✅ dict 增强：支持默认值
  dict: {
    f: (v, ds, p, def = '') => {
      const map = typeof ds === 'string' ? ds.split(p || '/') : ds
      return map[v] ?? def
    }
  },
  
  dateRange: {
    f: (v, d, k) => {
      const [start, end] = Array.isArray(v)
        ? (v.length > 1 ? [v[0], v[1]] : [v[0], v[0]])
        : [v, v]
      return [
        matchFormatMethod.string(dayjs(start).startOf(d || 'day'), k),
        matchFormatMethod.string(dayjs(end).endOf(d || 'day'), k)
      ]
    }
  },
}

/**
 * @desc  根据关键字查询函数（✅ 移除 !isEmpty(args[0]) 拦截，交由方法处理）
 * @param {String|Number} prop 搜索关键字
 * @return {Function}
 */
export const findMethod = (prop) => {
  let fmt = methods[prop]
  if (!fmt) {
    const k = Object.keys(methods).find(key => methods[key].kw?.includes(prop))
    if (k) fmt = methods[k]
  }
  if (fmt && fmt.f) {
    // ✅ 关键修复：不再提前拦截假值（如 0, false, ''），让 f 自行处理
    return (...args) => fmt.f(...args)
  }
}

export const formatMethods = new Proxy({}, { get: (target, prop) => findMethod(prop) })

/* 格式化方式匹配 */
export const matchFormatMethod = {
  function: (oVal, formatFn, ...args) => formatFn(oVal, ...args),

  string: (oVal, strFormat, templConf) => {
    if (!strFormat?.trim()) return oVal
    const val = typeof oVal === 'function' ? oVal() : oVal
    // ✅ 若 val 为 undefined/null/NaN，仍允许格式化（如 '0' → '000'），由方法决定
    // if (isEmpty(val)) return  // ← 原有逻辑已移除

    const exec = (val, batchStr, def) => {
      const steps = batchStr
        .split('&')
        .map(str => {
          const [f, ...argParts] = str.split(':')
          const argsStr = argParts.length ? argParts.join(':').trim() : ''
          return [formatMethods[f.trim()], argsStr ? argsStr.split(',').map(s => s.trim()) : []]
        })
        .filter(([fn]) => fn)

      if (steps.length === 0) return def
      return steps.reduce((v, [fn, args]) => fn(v, ...args), val)
    }

    const likeTempl = (templConf && ['_s', '_e', '_re'].some(k => k in templConf)) || strFormat.includes('#{')
    return (likeTempl
      ? templateEngine(strFormat, (item, opt) => exec(val, item, item), templConf)
      : exec(val, strFormat)
    ) ?? val
  },

  array: (oVal, arr, ...args) => arr.map(f => valueFormat(oVal, f, ...args)),

  object: (oVal, obj, ...args) => {
    if (obj.methods) {
      const fn = formatMethods[obj.methods]
      const fnArgs = obj.args
        ? (Array.isArray(obj.args) ? obj.args : [obj.args])
        : args
      return fn ? fn(oVal, ...fnArgs) : oVal
    }
    const val = obj.value
      ? (typeof obj.value === 'function' ? obj.value(oVal) : obj.value)
      : oVal
    return valueFormat(val, obj.format, ...args)
  },
}

export const valueFormat = (...args) => {
  if (args.length === 0) return undefined
  if (args.length === 1) return args[0]
  const fnType = Array.isArray(args[1]) ? 'array' : typeof args[1]
  return matchFormatMethod[fnType] ? matchFormatMethod[fnType](...args) : args[0]
}

export const formatValue = (...args) => valueFormat(args[1], args[0], ...args.slice(2))